package com.hongji.lms5xxcommunicator.realtime;

import com.hongji.lms5xxcommunicator.entity.ScanPointEntity;
import com.hongji.lms5xxcommunicator.grid.RasterGrid;
import com.hongji.lms5xxcommunicator.grid.core.GridSpec;
import com.hongji.lms5xxcommunicator.grid.export.GridDeltaPayload;
import com.hongji.lms5xxcommunicator.grid.export.GridPoint;

import java.util.*;

/**
 * 把后端数据映射为前端 DTO 的轻量 Mapper。
 * 统一“行起点”语义：由 rowBottomOrigin 控制（true=Bottom-origin；false=Top-origin）。
 */
public class PointDtoMapper {

    /** Three.Y = viewYBase - y */
    private final double viewYBase;
    /** Three.Z = z + viewZOffset */
    private final double viewZOffset;

    /** 行起点：true=Bottom-origin；false=Top-origin（与 HTTP 首帧保持一致） */
    private final boolean rowBottomOrigin;

    /** 兼容旧调用的构造：默认 bottom-origin（推荐保持与增量一致） */
    public PointDtoMapper(double viewYBase, double viewZOffset) {
        this(viewYBase, viewZOffset, true);
    }

    /** 新构造：由调用方传入行起点 */
    public PointDtoMapper(double viewYBase, double viewZOffset, boolean rowBottomOrigin) {
        this.viewYBase = viewYBase;
        this.viewZOffset = viewZOffset;
        this.rowBottomOrigin = rowBottomOrigin;
    }

    /** 构建 points（原始点）消息 DTO，携带 z 范围与版本 */
    public Map<String, Object> pointsMessage(boolean full, double volume,
                                             List<ScanPointEntity> batch, GridSpec spec,
                                             RasterGrid grid) {
        final List<Map<String, Object>> data = new ArrayList<>(batch != null ? batch.size() : 0);
        if (batch != null) {
            final int rows = spec.rows;
            for (ScanPointEntity p : batch) {
                if (p == null || p.getX()==null || p.getY()==null || p.getZ()==null) continue;
                int c = spec.toCol(p.getX());
                int r = spec.toRow(p.getY());
                if (rowBottomOrigin && r >= 0) r = rows - 1 - r; // ✅ 与增量/快照统一

                Map<String, Object> m = new HashMap<>(8);
                m.put("x", p.getX());                 // Three.X
                m.put("y", viewYBase - p.getY());     // Three.Y（翻转）
                m.put("z", p.getZ());                 // Three.Z（高度）
                m.put("r", r);
                m.put("c", c);
                data.add(m);
            }
        }
        Map<String, Object> dto = new HashMap<>(10);
        dto.put("type", "points");
        dto.put("full", full);
        dto.put("volume", volume);
        dto.put("data", data);
        dto.put("zMin", grid != null ? grid.getObservedMinZ() : 0.0);
        dto.put("zMax", grid != null ? grid.getObservedMaxZ() : 1.0);
        dto.put("rangeSeq", grid != null ? grid.getRangeSeq() : 0L);
        dto.put("rowOrigin", rowBottomOrigin ? "bottom" : "top"); // ✅ 可供前端自适配
        return dto;
    }

    /** 把“栅格增量”转为 rc 消息（同样携带 z 范围与版本） */
    public Map<String, Object> cellsDeltaMessage(GridDeltaPayload payload, GridSpec spec,
                                                 RasterGrid grid) {
        final List<GridPoint> ups = (payload != null && payload.getUpdates()!=null)
                ? payload.getUpdates() : Collections.emptyList();
        final int n = ups.size();
        final int rows = spec.rows;

        int[] r = new int[n];
        int[] c = new int[n];
        double[] z = new double[n];
        int[] cnt = new int[n];

        for (int i = 0; i < n; i++) {
            GridPoint gp = ups.get(i);
            int rr = gp.getRow();
            r[i] = rowBottomOrigin ? (rows - 1 - rr) : rr; // ✅ 与 points/HTTP 首帧一致
            c[i] = gp.getCol();
            z[i] = gp.getZ();         // 单位沿用 mm
            cnt[i] = gp.getCount();
        }

        Map<String, Object> cells = new HashMap<>(4);
        cells.put("r", r);
        cells.put("c", c);
        cells.put("z", z);
        cells.put("n", cnt);

        Map<String, Object> dto = new HashMap<>(12);
        dto.put("type", "delta");
        dto.put("full", payload != null && payload.isFull());
        dto.put("volume", payload != null ? payload.getVolume() : 0.0);
        dto.put("data", cells);
        dto.put("zMin", grid != null ? grid.getObservedMinZ() : 0.0);
        dto.put("zMax", grid != null ? grid.getObservedMaxZ() : 1.0);
        dto.put("rangeSeq", grid != null ? grid.getRangeSeq() : 0L);
        dto.put("rowOrigin", rowBottomOrigin ? "bottom" : "top"); // ✅
        return dto;
    }
}
